<template>
  <div class="canvasboard" :class="{ border: hasBorder }">
    <div class="mark" @click="isShowSize=false" v-if="isShowSize"></div>
    <div ref="html2canvas">
      <canvas class="drawing_board" ref="canvasImage"></canvas>
      <canvas class="drawing_board" ref="canvas" @mousedown="mousedown"></canvas>
      <div
        class="canvas_text"
        v-for="(item, i) in inputList"
        :key="i"
        :style="{
          top: item.top + 'px',
          left: item.left + 'px',
          border: item.border,
          cursor: item.cursor,
        }"
        @mousedown.stop.self="mousedownText($event, item)"
      >
        <textarea
          v-model="item.name"
          :style="{
            resize: item.resize,
            color: item.color,
            fontSize: item.fontSize + 'px',
          }"
          :disabled="currentTypeTool != 'text'"
          @focus="textareaFocus(item)"
        ></textarea>
      </div>
    </div>
    <div class="tools" style="left: 30px">
      <div class="tools-title">工具栏</div>
      <div
        class="tools-item"
        v-for="(item, i) in tools"
        :key="i"
        :title="item.name"
        :class="{ active: i == currentIndexTool }"
        @click="toggleTools(item.type, i)"
      >
        <i class="iconfont" :class="item.icon"></i>
        <div class="range_wrap" v-if="item.type == 'size' && isShowSize">
          <input
            type="range"
            min="1"
            max="30"
            v-model="pencleSize"
            title="调整笔刷粗细"
          />
          <div class="pencle_size">{{ pencleSize }}</div>
        </div>
      </div>
      <div @click="changePencleColor(-1)">
        <el-color-picker
          v-model="color"
          :class="{ active: currentIndexPencle == -1 }"
          @change="changePencleColor(-1)"
        ></el-color-picker>
      </div>
    </div>
    <div class="tools" style="right: 30px">
      <div class="tools-title">菜单栏</div>
      <div
        class="tools-item"
        v-for="(item, i) in menu"
        :key="i"
        :title="item.name"
        @click="toggleMenu(item.type)"
      >
        <i class="iconfont" :class="item.icon"></i>
      </div>
    </div>
    <div class="history_list">
      <el-drawer title="历史记录" :visible.sync="drawer" direction="btt">
        <div class="history_list-all" v-if="historyList.length > 0">
          <div
            class="history_list-item"
            v-for="(item, i) in historyList"
            :key="i"
            @click="showHistory(item)"
          >
            <img :src="item.url" alt="" />
            <span>{{ item.name }}</span>
            <i
              class="iconfont icon-lajixiang_huaban1"
              @click.stop.self="deleteHistory(item)"
            ></i>
          </div>
        </div>
        <div class="history_list-none" v-else>无记录</div>
      </el-drawer>
    </div>
  </div>
</template>
<script>
import html2canvas from "html2canvas";
import _ from "lodash";
let inputId = 0;
export default {
  name: "canvasBoard",
  props: {
    pencleList: {
      type: Array,
      default: () => {
        return ["#000", "#FF3333", "#0066FF", "#FFFF33", "#33CC66"];
      },
    },
    pencleSize: {
      type: Number,
      default: 5,
    },
    width: {
      type: Number,
      default: 800,
    },
    height: {
      type: Number,
      default: 600,
    },
    hasBorder: {
      type: Boolean,
      default: true,
    },
  },
  data() {
    return {
      color: "#000",
      tools: [
        { name: "画笔", type: "brush", icon: "icon-huabi_huaban1" },
        { name: "直线", type: "line", icon: "icon-zhixian_huaban1" },
        { name: "圆形", type: "cicle", icon: "icon-yuanquan_huaban1" },
        { name: "矩形", type: "rectangle", icon: "icon-juxinggongju_huaban1" },
        { name: "橡皮擦", type: "eraser", icon: "icon-xiangpi_huaban1" },
        { name: "文本", type: "text", icon: "icon-wenzi_huaban1" },
        { name: "撤销", type: "undo", icon: "icon-houtui" },
        { name: "清空", type: "clear", icon: "icon-lajixiang_huaban1" },
        { name: "粗细", type: "size", icon: "icon-cuxi" },
      ],
      menu: [
        { name: "导入图片", type: "import", icon: "icon-dakaiwenjianjia" },
        { name: "保存", type: "save", icon: "icon-save" },
        { name: "下载图片", type: "download", icon: "icon-xiazai4" },
        { name: "历史记录", type: "history", icon: "icon-history-fill" },
        { name: "退出", type: "exit", icon: "icon-tuichu_huaban1" },
      ],
      toolList: [
        { name: "画笔", type: "brush", icon: "icon-huabi_huaban1" },
        { name: "直线", type: "line", icon: "icon-zhixian_huaban1" },
        { name: "圆形", type: "cicle", icon: "icon-yuanquan_huaban1" },
        { name: "矩形", type: "rectangle", icon: "icon-juxinggongju_huaban1" },
        { name: "橡皮擦", type: "eraser", icon: "icon-xiangpi_huaban1" },
        { name: "文本", type: "text", icon: "icon-wenzi_huaban1" },
        { name: "撤销", type: "undo", icon: "icon-houtui" },
        { name: "清空", type: "clear", icon: "icon-lajixiang_huaban1" },
        { name: "导入", type: "import", icon: "icon-dakaiwenjianjia" },
        { name: "下载", type: "download", icon: "icon-xiazai4" },
        { name: "保存", type: "save", icon: "icon-save" },
        { name: "历史记录", type: "history", icon: "icon-history-fill" },
        { name: "退出", type: "exit", icon: "icon-tuichu_huaban1" },
      ],
      currentIndexPencle: 0,
      currentIndexTool: 0,
      currentTypeTool: "brush",
      historyData: [],
      currentBgColor: "#fff",
      inputList: [],
      currentInput: null,
      historyList: [],
      drawer: false,
      isShowSize: false,
    };
  },
  computed: {
    currentPencleColor() {
      return this.currentIndexPencle == -1
        ? this.color
        : this.pencleList[this.currentIndexPencle];
    },
  },
  watch: {
    pencleSize(val) {
      if (this.currentInput != null) {
        this.currentInput.fontSize = val;
      }
    },
  },
  methods: {
    showHistory(item) {
      this.drawer = false;
      this.clearCanvas(this.ctxImage);
      this.ctxImage.putImageData(item.canvas, 0, 0);
      this.inputList = _.cloneDeep(item.inputList);
    },
    saveHistory(data) {
      html2canvas(this.$refs.html2canvas, {
        x: 0,
        y: 0,
        width: this.canvas.width,
        height: this.canvas.height,
      }).then((canvas) => {
        let blob = this.dataURLToBlob(canvas.toDataURL("image/png"));
        this.historyList.push({
          id: _.uniqueId(),
          name: data || "未命名",
          canvas: this.ctxImage.getImageData(0, 0, this.canvas.width, this.canvas.height),
          inputList: _.cloneDeep(this.inputList),
          url: URL.createObjectURL(blob),
        });
      });
    },
    deleteHistory(item) {
      this.$confirm("确定删除吗?", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning",
      })
        .then(() => {
          this.historyList = _.reject(_.cloneDeep(this.historyList), ["id", item.id]);
        })
        .catch(() => {});
    },
    initInput() {
      return new Promise((res) => {
        this.currentInput = null;
        _.forEach(this.inputList, (item) => {
          item.border = "0 none";
          item.resize = "none";
          item.cursor = "auto";
        });
        res();
      });
    },
    changePencleColor(i) {
      this.currentIndexPencle = i;
      if (this.currentInput != null) {
        this.currentInput.color = this.currentPencleColor;
      }
    },
    toggleTools(type, i) {
      this.initInput();
      this.isShowSize=false;
      if (type == "undo") {
        let _lastData = _.last(this.historyData);
        if (_.size(this.historyData) > 0) {
          if (Object.keys(_lastData).length > 1) {
            this.inputList.pop();
          } else {
            this.ctxImage.putImageData(_lastData, 0, 0);
          }
          this.historyData.pop();
        }
      } else if (type == "clear") {
        this.fillCanvas(this.ctxImage, "#fff");
        this.historyData = [];
        this.inputList = [];
        this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
      } else if (type == "size") {
        this.isShowSize = true;
      } else {
        this.currentIndexTool = i;
        this.currentTypeTool = type;
      }
    },
    toggleMenu(type) {
      this.initInput();
      this.isShowSize=false;
      if (type == "import") {
        this.importImage();
      } else if (type == "download") {
        this.saveImage();
      } else if (type == "save") {
        this.$prompt("创建历史记录", {
          confirmButtonText: "确定",
          cancelButtonText: "取消",
        })
          .then((data) => {
            this.saveHistory(data.value);
          })
          .catch(() => {});
      } else if (type == "history") {
        this.drawer = true;
      } else if (type == "exit") {
        this.$emit("exit");
      }
    },
    clickToolHandle(type, i) {
      this.initInput();
      if (type == "fill") {
        this.currentBgColor = this.currentPencleColor;
        this.fillCanvas(this.ctx, this.currentBgColor);
        this.addHistoryData(false);
      } else if (type == "undo") {
        let _lastData = _.last(this.historyData);
        if (_.size(this.historyData) > 0) {
          if (Object.keys(_lastData).length > 1) {
            this.inputList.pop();
          } else {
            this.ctxImage.putImageData(_lastData, 0, 0);
          }
          this.historyData.pop();
        }
      } else if (type == "clear") {
        this.fillCanvas(this.ctxImage, "#fff");
        this.historyData = [];
        this.inputList = [];
        this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
      } else if (type == "import") {
        this.importImage();
      } else if (type == "download") {
        this.saveImage();
      } else if (type == "save") {
        this.$prompt("创建历史记录", {
          confirmButtonText: "确定",
          cancelButtonText: "取消",
        })
          .then((data) => {
            this.saveHistory(data.value);
          })
          .catch(() => {});
      } else if (type == "history") {
        this.drawer = true;
      } else if (type == "exit") {
        this.$emit("exit");
      } else {
        this.currentIndexTool = i;
        this.currentTypeTool = type;
      }
    },
    addHistoryData(type) {
      if (type) {
        this.historyData.push(this.currentInput);
      } else {
        var image = new Image();
        image.src = this.canvas.toDataURL();
        image.onload = () => {
          this.ctxImage.drawImage(
            image,
            0,
            0,
            image.width,
            image.height,
            0,
            0,
            this.canvas.width,
            this.canvas.height
          );
          this.clearCanvas(this.ctx);
        };
        this.historyData.push(
          this.ctxImage.getImageData(0, 0, this.canvas.width, this.canvas.height)
        );
      }
    },
    initCanvas() {
      this.canvas = this.$refs.canvas;
      this.canvasImage = this.$refs.canvasImage;
      this.ctx = this.canvas.getContext("2d");
      this.ctxImage = this.canvasImage.getContext("2d");

      [this.canvas.width, this.canvas.height] = this.getWidthHeight();
      [this.canvasImage.width, this.canvasImage.height] = this.getWidthHeight();

      this.fillCanvas(this.ctx, "#fff");

      this.ctx.lineCap = "round";
      this.ctx.lineJoin = "round";
      this.ctxImage.lineCap = "round";
      this.ctxImage.lineJoin = "round";
    },
    drawPath(context, callback, beginPath = false) {
      context.lineWidth = this.pencleSize;
      context.strokeStyle = this.currentPencleColor;
      if (beginPath) {
        context.globalCompositeOperation = "copy";
        context.beginPath();
      }
      callback();
      context.stroke();
      context.closePath();
    },
    clearCanvas(context) {
      context.clearRect(0, 0, this.canvas.width, this.canvas.height);
    },
    fillCanvas(context, color) {
      context.fillStyle = color;
      context.fillRect(0, 0, this.canvas.width, this.canvas.height);
    },
    textareaFocus(item) {
      if (this.currentTypeTool == "text") {
        this.initInput();
        item.border = "1px dashed #ccc";
        item.resize = "auto";
        item.cursor = "move";
        this.currentInput = item;
      }
    },
    dataURLToBlob(url) {
      let arr = url.split(",");
      let mime = arr[0].match(/:(.*?);/)[1];
      let str = atob(arr[1]);
      let length = _.size(str);
      let u8arr = new Uint8Array(length);
      while (length--) {
        u8arr[length] = str.charCodeAt(length);
      }
      return new Blob([u8arr], { type: mime });
    },
    importImage() {
      let input = document.createElement("input");
      input.addEventListener(
        "change",
        () => {
          let file = input.files[0];
          let reader = new FileReader();
          reader.readAsArrayBuffer(file);
          reader.onload = () => {
            var blob = new Blob([reader.result], { type: "image/jpeg" });
            var img = new Image();
            img.src = URL.createObjectURL(blob);
            img.onload = () => {
              this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
              this.ctx.drawImage(img, 0, 0, this.canvas.width, this.canvas.height);
            };
          };
        },
        false
      );
      input.type = "file";
      input.accept = "image/*";
      input.click();
    },
    async saveImage() {
      await this.initInput();
      let tag = document.createElement("a");
      document.body.appendChild(tag);
      html2canvas(this.$refs.html2canvas, {
        x: 0,
        y: 0,
        width: this.canvas.width,
        height: this.canvas.height,
      }).then((canvas) => {
        let dom = document.body.appendChild(canvas);
        let blob = this.dataURLToBlob(dom.toDataURL("image/png"));

        tag.setAttribute("href", URL.createObjectURL(blob));
        tag.setAttribute("download", "zspic" + new Date().getTime());
        tag.setAttribute("target", "_blank");
        tag.click();
        URL.revokeObjectURL(blob);

        document.body.removeChild(dom);
        document.body.removeChild(tag);
      });
    },

    getWidthHeight() {
      return [
        document.documentElement.clientWidth,
        document.documentElement.clientHeight,
      ];
    },
    mousedown(e) {
      if (this.currentTypeTool == "text") {
        this.initInput();
        inputId++;
        this.currentInput = {
          id: inputId,
          name: "文本",
          left: e.clientX,
          top: e.clientY,
          border: "1px dashed #ccc",
          resize: "auto",
          color: this.currentPencleColor,
          fontSize: this.pencleSize,
          cursor: "move",
        };
        this.inputList.push(this.currentInput);
      } else {
        this.initInput();
        this.painting = true;
        this.ctx.save();
        this.ctx.beginPath();
        this.ctxImage.beginPath();
        [this.lastPointX, this.lastPointY] = [e.clientX, e.clientY];
      }
      document.onmousemove = (e) => {
        if (!this.painting) return;
        if (this.currentTypeTool == "brush") {
          this.drawPath(this.ctx, () => {
            this.ctx.moveTo(this.lastPointX, this.lastPointY);
            this.ctx.lineTo(e.clientX, e.clientY);
          });
          [this.lastPointX, this.lastPointY] = [e.clientX, e.clientY];
        }
        if (this.currentTypeTool == "line") {
          this.drawPath(
            this.ctx,
            () => {
              this.ctx.moveTo(this.lastPointX, this.lastPointY);
              this.ctx.lineTo(e.clientX, e.clientY);
            },
            true
          );
        }
        if (this.currentTypeTool == "cicle") {
          let [x, y] = [e.clientX - this.lastPointX, e.clientY - this.lastPointY];
          let sqrt = Math.sqrt(x * x + y * y);
          this.drawPath(
            this.ctx,
            () => {
              this.ctx.arc(this.lastPointX, this.lastPointY, sqrt, 0, 2 * Math.PI);
            },
            true
          );
        }
        if (this.currentTypeTool == "rectangle") {
          this.drawPath(
            this.ctx,
            () => {
              this.ctx.rect(
                this.lastPointX,
                this.lastPointY,
                e.clientX - this.lastPointX,
                e.clientY - this.lastPointY
              );
            },
            true
          );
        }

        if (this.currentTypeTool == "eraser") {
          this.drawPath(this.ctx, () => {
            this.ctx.strokeStyle = "#fff";
            this.ctx.moveTo(this.lastPointX, this.lastPointY);
            this.ctx.lineTo(e.clientX, e.clientY);
          });
          [this.lastPointX, this.lastPointY] = [e.clientX, e.clientY];
        }
      };
      this.canvas.onmouseup = () => {
        this.painting = false;
        this.addHistoryData(this.currentTypeTool == "text");
      };
    },
    mousedownText(e, item) {
      if (
        this.currentTypeTool == "text" &&
        (!this.textMove || this.textMove == undefined)
      ) {
        this.startX = e.clientX - item.left;
        this.startY = e.clientY - item.top;
        this.textMove = true;
        this.initInput();
        item.border = "1px dashed #ccc";
        item.resize = "auto";
        item.cursor = "move";
        this.currentInput = item;
      }

      document.onmousemove = (e) => {
        if (
          this.textMove &&
          this.currentTypeTool == "text" &&
          this.currentInput?.id == item.id
        ) {
          item.left = e.clientX - this.startX;
          item.top = e.clientY - this.startY;
        }
      };
      document.onmouseup = () => {
        this.textMove = false;
      };
    },
  },
  mounted() {
    this.initCanvas();
  },
};
</script>
<style lang="scss" scoped>
$mainColor: #00cb95;
@mixin wh($w, $h) {
  width: $w;
  height: $h;
}
.tools {
  box-shadow: 0 0 8px $mainColor;
  position: fixed;
  top: 50%;
  transform: translateY(-50%);
  z-index: 3;
  background-color: #fff;
  &-title {
    @include wh(50px, 40px);
    line-height: 40px;
    text-align: center;
    font-size: 14px;
    background-color: $mainColor;
    color: #fff;
  }
  &-item {
    position: relative;
    @include wh(50px, 50px);
    line-height: 50px;
    border-bottom: 1px solid $mainColor;
    box-sizing: border-box;
    text-align: center;
    cursor: pointer;
    color: #777777;
    transition: 0.3s;
    &:active {
      color: $mainColor;
    }
  }
  .active {
    color: $mainColor;
  }
  .iconfont {
    font-size: 28px;
  }
  .range_wrap {
    position: absolute;
    right: -210px;
    top: 0;
    @include wh(200px, 50px);
    background-color: #fff;
    box-shadow: 0 0 8px $mainColor;
    display: flex;
    .pencle_size {
      @include wh(30px, 50px);
      line-height: 50px;
      font-size: 14px;
      text-align: center;
      color: $mainColor;
    }
    input {
      @include wh(150px, 20px);
      margin-left: 5px;
      transform-origin: 75px 75px;
      -webkit-appearance: none;
      outline: none;
      position: relative;
      &::after {
        display: block;
        content: "";
        @include wh(0, 0);
        border: 5px solid transparent;
        border-right: 150px solid $mainColor;
        border-left-width: 0;
        position: absolute;
        left: 0;
        top: 20px;
        border-radius: 15px;
        z-index: 0;
      }
    }
    input[type="range"]::-webkit-slider-thumb,
    input[type="range"]::-moz-range-thumb {
      -webkit-appearance: none;
    }
    input[type="range"]::-webkit-slider-runnable-track,
    input[type="range"]::-moz-range-track {
      height: 10px;
      border-radius: 10px;
      box-shadow: none;
    }
    input[type="range"]::-webkit-slider-thumb {
      -webkit-appearance: none;
      @include wh(20px, 20px);
      margin-top: 30px;
      background: #ffffff;
      border-radius: 50%;
      box-shadow: 0 0 8px $mainColor;
      position: relative;
      z-index: 999;
      cursor: pointer;
    }
  }
}
.mark {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 2;
}
.canvasboard {
  position: relative;
  overflow: hidden;
  .drawing_board {
    position: fixed;
    display: block;
    cursor: crosshair;
  }
  .mark {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
}
}
.canvas_text {
  position: fixed;
  padding: 10px;
  border-radius: 10px;
  textarea {
    outline: none;
    border: 0 none;
    background-color: rgba(0, 0, 0, 0);
    overflow: hidden;
  }
}

.history_list {
  &-all {
    margin: 0 30px;
    display: flex;
    overflow-x: auto;
  }
  &-item {
    @include wh(192px, 170px);
    margin-right: 10px;
    flex: none;
    text-align: center;
    position: relative;
    cursor: pointer;
    img {
      @include wh(192px, 108px);
      vertical-align: middle;
      border: 1px solid #ccc;
    }
    span {
      vertical-align: middle;
      line-height: 26px;
      font-size: 16px;
    }
    .iconfont {
      color: #72767b;
      position: absolute;
      font-size: 26px;
      top: 5px;
      right: 5px;
    }
  }
  &-none {
    margin: 0 30px;
    text-align: center;
    font-size: 18px;
    line-height: 100px;
  }
}
</style>
<style lang="scss">
$mainColor: #00cb95;
@mixin wh($w, $h) {
  width: $w;
  height: $h;
}

.canvasboard {
  .el-color-picker {
    @include wh(50px, 50px);
  }
  .el-color-picker__trigger {
    @include wh(50px, 50px);
    box-sizing: border-box;
    border: 3px solid #fff;
    cursor: pointer;
    transition: 0.3s;
    padding: 0;
  }
  .el-color-picker__color {
    overflow: hidden;
    border: 0 none;
  }
}
</style>
